#include <MaxStream.hpp>

MaxExporter::MaxStream::~MaxStream()
{
	this->tempMaterial.clear();
}

MaxExporter::MaxStream::MaxStream(INode* iroot)
{
	this->iroot = iroot;
}

size_t MaxExporter::MaxStream::write(Modeling::Scene* scene)
{
	return 0;
}

size_t MaxExporter::MaxStream::write(Modeling::Mesh* mesh)
{
	return 0;
}

size_t MaxExporter::MaxStream::read(Modeling::Scene* scene)
{
	file = fopen("f:/project/filer.txt", "w");
	this->scene = scene;
	this->root = scene->createRoot();
	this->root->setName(this->iroot->GetName());

	// Read the animation data
	Modeling::Animation* animation = scene->createAnimation();
	this->readAnimation(animation);

	// Read the scnene graph data
	this->readNode(iroot);

	fprintf(file, "root->getMaterialsQty(): %d\n", root->getMaterialsQty());
	fclose(file);
	return 0;
}

size_t MaxExporter::MaxStream::readAnimation(Modeling::Animation* animation)
{
	unsigned int FPS = 24;
	unsigned int frames = 0;

	this->animInterval = GetCOREInterface()->GetAnimRange();
	this->framesInterval = SecToTicks(1.0f/FPS);
	for (TimeValue t=this->animInterval.Start() ; t<this->animInterval.End() ; t+=this->framesInterval) {
		frames++;
	}
	animation->setFrames(frames);
	animation->setFPS(FPS);

	return 0;
}

size_t MaxExporter::MaxStream::readNode(INode* inode)
{
	for (int i=0 ; i<inode->NumberOfChildren() ; i++) {
		INode* ichild = inode->GetChildNode(i);
		
		this->readMaterial(ichild);

		Object* object = ichild->GetObjectRef();
		if (object != NULL) {
			if (object->CanConvertToType(triObjectClassID)) {
				TriObject* triObject = (TriObject*)object->ConvertToType(0, triObjectClassID);
				if (triObject != NULL) {
					// extract data.
					Modeling::Mesh* mesh = this->root->addMesh(inode->GetName(), ichild->GetName());
					Mesh& iMesh = triObject->GetMesh();
					
					this->readMatrices(ichild, mesh);
					this->readVertices(iMesh, mesh);
					this->readFaces(iMesh, mesh);
					this->readTexCoords(iMesh, mesh);

					if (object != triObject) {
						triObject->DeleteMe();
					}
				}
			}
		}
		this->tempMaterial.clear();
		this->readNode(ichild);
	}
	return 0;
}

size_t MaxExporter::MaxStream::readMaterial(INode* node)
{
	fprintf(file, "%s Materials:\n", node->GetName());
	Mtl* mtl = node->GetMtl();
	if (mtl != NULL) {
		this->castMaterial(mtl);
	}

	return 0;
}

void MaxExporter::MaxStream::castMaterial(Mtl* mtl)
{
	if (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) {
		fprintf(file, "DMTL_CLASS_ID\n");
		this->addMaterial(mtl);
	} else if ( mtl->ClassID() == Class_ID(MULTI_CLASS_ID, 0) ) {
		fprintf(file, "MULTI_CLASS_ID\n");
		fprintf(file,"NumSubMtls(): %d\n", mtl->NumSubMtls());
		for (unsigned int i=0 ; i<mtl->NumSubMtls() ; i++) {
			this->castMaterial(mtl->GetSubMtl(i));
		}
	}
}

void MaxExporter::MaxStream::addMaterial(Mtl* mtl)
{
	const char* name = mtl->GetName();
	Modeling::Material* material = this->root->addMaterial(name);
	this->tempMaterial.push_back(material);

	Modeling::RGBf ambient(mtl->GetAmbient().r, mtl->GetAmbient().g, mtl->GetAmbient().b);
	material->setAmbient(ambient);

	Modeling::RGBf diffuse(mtl->GetDiffuse().r, mtl->GetDiffuse().g, mtl->GetDiffuse().b);
	material->setDiffuse(diffuse);

	Modeling::RGBf specular(mtl->GetSpecular().r, mtl->GetSpecular().g, mtl->GetSpecular().b);
	material->setSpecular(specular);

	float shininess = mtl->GetShininess();
	material->setShininess(100*shininess);

	fprintf(file, "mtl->GetName(): %s\n", name);
	fprintf(file, "ambient: [%f, %f, %f]\n", material->getAmbient()->red(), material->getAmbient()->green(), material->getAmbient()->blue());
	fprintf(file, "diffuse: [%f, %f, %f]\n", material->getDiffuse()->red(), material->getDiffuse()->green(), material->getDiffuse()->blue());
	fprintf(file, "specular: [%f, %f, %f]\n", material->getSpecular()->red(), material->getSpecular()->green(), material->getSpecular()->blue());
	fprintf(file, "shininess: %f\n", shininess);

	for (int i=0 ; i< mtl->NumSubTexmaps() ; i++) {
		Texmap* tMap = mtl->GetSubTexmap(i);
		if (tMap != NULL) {
			MSTR slotName = tMap->GetSubTexmapSlotName(i);
			MSTR tvName = tMap->GetSubTexmapTVName(i);
			fprintf(file, "slot-name: %s\n", slotName.data());
			fprintf(file, "tv-name: %s\n", tvName.data());
			MSTR className;
			tMap->GetClassName(className);
			fprintf(file, "Tex: %s\n", className.data());
			if (tMap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
				BitmapTex* bitMap = (BitmapTex*)tMap;
				fprintf(file, "MapName: %s\n", bitMap->GetMapName());
				Modeling::Map* diffuseMap = material->enableDiffuseMap();
				diffuseMap->setFileName(bitMap->GetMapName());
			}
		}
	}
}

size_t MaxExporter::MaxStream::readMatrices(INode* inode, Modeling::Node* node)
{
	for (TimeValue t=this->animInterval.Start() ; t<this->animInterval.End() ; t+=this->framesInterval) {
		Matrix3 tmWorld = inode->GetNodeTM(t);
		Matrix3 tmParent = inode->GetParentTM(t);
		Matrix3 tmLocal = tmWorld * Inverse(tmParent);
		Mathing::Matrix4f mat;
		mat[0] = Vector4f( tmLocal.GetRow(0).x,  tmLocal.GetRow(0).z, -tmLocal.GetRow(0).y, 0.0f );
		mat[1] = Vector4f( tmLocal.GetRow(2).x,  tmLocal.GetRow(2).z, -tmLocal.GetRow(2).y, 0.0f );
		mat[2] = Vector4f(-tmLocal.GetRow(1).x, -tmLocal.GetRow(1).z,  tmLocal.GetRow(1).y, 0.0f );
		mat[3] = Vector4f( tmLocal.GetRow(3).x,  tmLocal.GetRow(3).z, -tmLocal.GetRow(3).y, 1.0f );
		node->addMatrix(mat);
	}

	return 0;
}

size_t MaxExporter::MaxStream::readVertices(Mesh& iMesh, Modeling::Mesh* mesh)
{
	int nVerts = iMesh.getNumVerts();
	for (int j=0 ; j<nVerts ; j++) {
		Point3& vtx = iMesh.getVert(j);
		Vector3f* vertex = mesh->addVertex( Mathing::Vector3f((float)vtx.x, (float)vtx.z, (float)(-vtx.y)) );
	}
	return 0;
}

size_t MaxExporter::MaxStream::readFaces(Mesh& iMesh, Modeling::Mesh* mesh)
{
	int nFaces = iMesh.getNumFaces();
	std::vector<DWORD> smoothGroup;
	for (int j=0 ; j<nFaces ; j++) {
		Face& lFace = iMesh.faces[j];
		DWORD* pIndices = lFace.getAllVerts();

		Mathing::Vector3f* v1 = mesh->getVertex(pIndices[0]);
		Mathing::Vector3f* v2 = mesh->getVertex(pIndices[1]);
		Mathing::Vector3f* v3 = mesh->getVertex(pIndices[2]);

		Modeling::Polygon* polygon = mesh->addPolygon(v1, v2, v3);
		smoothGroup.push_back(lFace.getSmGroup());

		// If the node has assigned a single material (DMTL_CLASS_ID)
		if (this->tempMaterial.size() == 1) {
			Modeling::Material* material = this->tempMaterial[0];
			polygon->setMaterial(material);
			//fprintf(file, "material->getName(): %s\n", material->getName().c_str());
			//fprintf(file, "P[%d]: %d\n", j, lFace.getMatID());

		// Else If the node has assigned a multiple material (MULTI_CLASS_ID)
		} else if (this->tempMaterial.size() > 1) {
			Modeling::Material* material = this->tempMaterial[lFace.getMatID()];
			polygon->setMaterial(material);
			//fprintf(file, "material->getName(): %s\n", material->getName().c_str());
			//fprintf(file, "P[%d]: %d\n", j, lFace.getMatID());
		}
	}
	this->generateNormals(mesh, smoothGroup);
	smoothGroup.clear();

	return 0;
}

size_t MaxExporter::MaxStream::readTexCoords(Mesh& iMesh, Modeling::Mesh* mesh)
{
	fprintf(file, "iMesh.getNumTVerts(): %d\n", iMesh.getNumTVerts());
	fprintf(file, "iMesh.getNumVerts(): %d\n", iMesh.getNumVerts());
	for (unsigned int i=0 ; i<iMesh.getNumTVerts() ; i++) {
		UVVert& uvVert = iMesh.getTVert(i);
		fprintf(file, "TVert(%f, %f)\n", uvVert.x, uvVert.y);
		mesh->addMapCoord(Vector2f(uvVert.x, uvVert.y));
	}

	return 0;
}

void MaxExporter::MaxStream::generateNormals(Modeling::Mesh* mesh, std::vector<DWORD>& smoothGroup)
{
	//si ( (algun vertice de polygon[i] == algun vertice de polygon[j] ) && (share smoothing group) ) 
	//normal del vertice de polygon[i] += normal del polygon[j]
	
	unsigned int polygonsQty = mesh->getPolygonsQty();
	for ( unsigned int i=0 ; i<polygonsQty - 1 ; i++ ) {
		for ( unsigned int j=i+1 ; j<polygonsQty ; j++ ) {
			for (unsigned int k=0 ; k<3 ; k++) {
				for (unsigned int l=0 ; l<3 ; l++) { 

					Mathing::Vector3f v1 = (*mesh->getPolygon(i)->getVertex(k)); 
					Mathing::Vector3f v2 = (*mesh->getPolygon(j)->getVertex(l)); 
					DWORD smth1 = smoothGroup[i];
					DWORD smth2 = smoothGroup[j];
					
					if ((v1 == v2) && (smth1 == smth2)) {
						Mathing::Vector3f n1 = mesh->getPolygon(j)->getPolygonNormal();
						*mesh->getPolygon(i)->getNormal(k) += n1;

						Mathing::Vector3f n2 = mesh->getPolygon(i)->getPolygonNormal();
						*mesh->getPolygon(j)->getNormal(l) += n2;
					}
				}
			}
		}
	}
}

size_t MaxExporter::MaxStream::read(Modeling::Mesh* mesh)
{
	return 0;
}
